﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using AlphaMode = SharpDX.Direct2D1.AlphaMode;
using Bitmap = SharpDX.Direct2D1.Bitmap;
using Device = SharpDX.Direct3D11.Device;
using Device1 = SharpDX.Direct3D11.Device1;
using PixelFormat = SharpDX.Direct2D1.PixelFormat;
using RectangleF = SharpDX.RectangleF;

namespace DirecxPictureBox
{
    public partial class DirectxPictureBox : UserControl
    {
  
        public DirectxPictureBox()
        {
       
            InitializeComponent();
            Initdx();
            _lsize = ClientSize;
        }

        private readonly object _lo = new object();
        private bool draw = false;
        public void Reresh()
        {
           

            if (!draw)
            {
                draw = true;
                lock (_lo)
                {

                    _d2DContext.Target = _d2DTarget;
                    _d2DContext.BeginDraw();

                    if (SizeMode==PictureBoxSizeMode.StretchImage)
                    _d2DContext.DrawBitmap(_bitmap,
                        new RectangleF(0, 0, _d2DContext.Size.Width, _d2DContext.Size.Height), 1,
                        BitmapInterpolationMode.Linear);

                    if (SizeMode == PictureBoxSizeMode.CenterImage)
                        _d2DContext.DrawBitmap(_bitmap,
                            new RectangleF(_d2DContext.Size.Width / 2 - _bitmap.Size.Width / 2, _d2DContext.Size.Height / 2 - _bitmap.Size.Height / 2, _bitmap.Size.Width, _bitmap.Size.Height), 1,
                            BitmapInterpolationMode.Linear);

                    if (SizeMode == PictureBoxSizeMode.Normal)
                        _d2DContext.DrawBitmap(_bitmap,
                            new RectangleF(0,0, _bitmap.Size.Width, _bitmap.Size.Height), 1,
                            BitmapInterpolationMode.Linear);

                    if (SizeMode == PictureBoxSizeMode.Zoom)
                    {
                        double kw = Convert.ToDouble(_bitmap.Size.Width) / Width;
                        double kh = Convert.ToDouble(_bitmap.Size.Height) / Height;
                       double k =1/ Math.Max(kw, kh);
                       _d2DContext.DrawBitmap(_bitmap,
                                                new RectangleF(_d2DContext.Size.Width / 2 -Convert.ToInt32 (_bitmap.Size.Width*k / 2), _d2DContext.Size.Height / 2 - Convert.ToInt32 (_bitmap.Size.Height*k / 2),Convert.ToInt32 (k* _bitmap.Size.Width), Convert.ToInt32 (k*_bitmap.Size.Height)), 1,
                                                BitmapInterpolationMode.Linear);
                    }
                    _d2DContext.EndDraw();
                    _swapChain.Present(1, PresentFlags.None);
                }
                draw = false;
            }

        }

        public PictureBoxSizeMode SizeMode = PictureBoxSizeMode.Zoom;

        private Bitmap _bitmap;
        private Device _device;
        private SharpDX.Direct2D1.DeviceContext _d2DContext;
        private SwapChain1 _swapChain;
        private Bitmap _d2DTarget;
        private SwapChainDescription1 _description;
        private Factory2 _dxgiFactory2;
        private BitmapProperties1 _properties;
        private Device2 _dxgiDevice2;
        private Adapter _dxgiAdapter;
        private SharpDX.Direct2D1.Device _d2DDevice;
        private Surface _backBuffer;
        private Device _defaultDevice; 
        private void Initdx()
        {
            _defaultDevice = new Device(DriverType.Hardware, DeviceCreationFlags.BgraSupport);
            _device = _defaultDevice.QueryInterface<Device1>();
            _dxgiDevice2 = _device.QueryInterface<Device2>();
       
            _dxgiAdapter = _dxgiDevice2.Adapter;
            
            _description = new SwapChainDescription1()
            {
                Flags = SwapChainFlags.GdiCompatible,
                Format = Format.B8G8R8A8_UNorm,
                Stereo = false,
                SampleDescription = new SampleDescription(1, 0),
                Usage = Usage.RenderTargetOutput,
                BufferCount = 2,
                Scaling = Scaling.None,
                SwapEffect = SwapEffect.FlipSequential,
            };
            _dxgiFactory2 = _dxgiAdapter.GetParent<Factory2>();
            _d2DDevice = new SharpDX.Direct2D1.Device(_dxgiDevice2);
            _d2DContext = new SharpDX.Direct2D1.DeviceContext(_d2DDevice, DeviceContextOptions.EnableMultithreadedOptimizations);
            _properties = new BitmapProperties1(new PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Ignore), 96, 96, BitmapOptions.Target | BitmapOptions.CannotDraw);
            _swapChain = _dxgiFactory2.CreateSwapChainForHwnd(_device, Handle, ref _description, null, null);
            _backBuffer = _swapChain.GetBackBuffer<Surface>(0);
            _d2DTarget = new Bitmap1(_d2DContext, _backBuffer, _properties);
           
        }

        public void ShowImage(System.Drawing.Bitmap bitmap)
        {
            lock (_lo)
            {
                if (_bitmap != null) _bitmap.Dispose();
                _bitmap = LoadFromBitmap1(_d2DContext, bitmap);

            }
       //    Reresh();

        }

        public void ShowImage(byte[] bitmap, int w, int h, int stride)
        {
            lock (_lo)
            {
                if (_bitmap != null) _bitmap.Dispose();
                _bitmap = LoadFromBitmap2(_d2DContext, bitmap, w, h, stride);

            }
            Reresh();

        }

        private static Bitmap LoadFromBitmap1(RenderTarget renderTarget, System.Drawing.Bitmap bitmap)
        {
            var sourceArea = new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height);
           
            var bitmapProperties = new BitmapProperties(new PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Ignore));

            var bitmapData = bitmap.LockBits(sourceArea, ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
              
                var b = new Bitmap(renderTarget, new Size2(bitmap.Width, bitmap.Height), bitmapProperties);
                b.CopyFromMemory(bitmapData.Scan0, bitmapData.Stride);
                bitmap.UnlockBits(bitmapData);
            
            return b;



        }

        private static Bitmap LoadFromBitmap2(RenderTarget renderTarget, byte[] bitmap, int w, int h, int stride)
        {

            var bitmapProperties = new BitmapProperties(new PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Ignore));
            var b = new Bitmap (renderTarget, new Size2(w, h), bitmapProperties);
            b.CopyFromMemory(bitmap, stride);
           
           

            return b;



        }

        private static Bitmap LoadFromBitmap(RenderTarget renderTarget, System.Drawing.Bitmap bitmap)
        {
           
           
                var sourceArea = new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height);
                var bitmapProperties = new BitmapProperties(new PixelFormat(Format.R8G8B8A8_UNorm, AlphaMode.Premultiplied));
                var size = new Size(bitmap.Width, bitmap.Height);
                var stride = bitmap.Width * sizeof(int);
                using (var tempStream = new DataStream(bitmap.Height * stride, true, true))
                {
                    var bitmapData = bitmap.LockBits(sourceArea, ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
                    for (var y = 0; y < bitmap.Height; y++)
                    {
                        var offset = bitmapData.Stride * y;
                        for (var x = 0; x < bitmap.Width; x++)
                        {
                            var B = Marshal.ReadByte(bitmapData.Scan0, offset++);
                            var G = Marshal.ReadByte(bitmapData.Scan0, offset++);
                            var R = Marshal.ReadByte(bitmapData.Scan0, offset++);
                            var A = Marshal.ReadByte(bitmapData.Scan0, offset++);
                            var rgba = R | (G << 8) | (B << 16) | (A << 24);
                            tempStream.Write(rgba);
                        }
                    }
                    bitmap.UnlockBits(bitmapData);
                    tempStream.Position = 0;
                    return new Bitmap(renderTarget, new Size2(size.Width, size.Height), tempStream, stride, bitmapProperties);
                }
        }

        private Size _lsize;
      
        private void timer1_Tick(object sender, EventArgs e)
        {
            if (_lsize != ClientSize)
            {
                lock (_lo)
                {
                    _lsize = ClientSize;
                    Uninitialize();
                    Initdx();
                }
                Refresh();
            }
        }


        public void Uninitialize()
        {
      
            _device.Dispose();
            _d2DContext.Dispose();
            _swapChain.Dispose();
            _d2DTarget.Dispose();
            _dxgiFactory2.Dispose();
            _dxgiDevice2.Dispose();
            _dxgiAdapter.Dispose();
            _d2DDevice.Dispose();
            _backBuffer.Dispose();
            _defaultDevice.Dispose(); 
        }

        private void DirectxPictureBox_Load(object sender, EventArgs e)
        {

        }

    }
}
